
import { AnimationAction, AnimationMixer, Clock, Group, Mesh, Object3D, Vector3 } from "three";
import threeLoader from "three-base/threeLoader";
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';

export default class People extends Group {
    constructor(url?: string) {
        super();
        url && this.loadModel(url);
    }

    url = "";

    // 模型对象
    static models: Map<string, Object3D> = new Map();

    // 模型是否在加载中
    static loadModeling: Map<string, boolean> = new Map();

    // 加载完成
    loaded = false;

    // 是否有动画
    hasAnimation = false;

    //动画混合器
    mixer?: AnimationMixer;

    //动画剪辑对象数组
    animationActions: { [key: string]: AnimationAction } = {};

    // 获取文件名的后缀
    getSuffix(filename: string) {
        const match = filename.match(/\.([^\.]+)$/);
        return match ? match[1].toLowerCase() : '';
    }

    // 如果url 是blob地址的话,就用suffix字段来制定模型的类型
    async loadModel(url: string, filename: string = "") {

        this.url = url;

        if (!url) {
            this.clear();
            return;
        }

        this.loaded = false;

        // 没有模型并且没有加载任务时就去加载模型
        if (!People.models.get(url) && !People.loadModeling.get(url)) {
            People.loadModeling.set(url, true);

            // 万能加载器
            let modal = await threeLoader.loadModel(url, filename);

            People.models.set(url, modal);
            People.loadModeling.set(url, false);
        }

        // 如果模型还在加载中, 就等待模型加载完成
        do {
            await new Promise(resolve => setTimeout(resolve, 50));
        } while (People.loadModeling.get(url));

        if (People.models.get(url)) {

            this.clear();
            const mesh = People.models.get(url)!;
            // 判断是否有动画
            this.hasAnimation = mesh.animations.length > 0;

            if (this.hasAnimation) {
                const model = SkeletonUtils.clone(mesh);
                this.mixer = new AnimationMixer(model);

                this.animationActions = {};
                mesh.animations.map(animation => {
                    const animationAction = this.mixer!.clipAction(animation).play();
                    animationAction.weight = 0;
                    this.animationActions[animation.name] = animationAction;
                });

                this.add(model);
            } else {
                this.add(mesh.clone(true));
            }

        }

        await new Promise(resolve => requestAnimationFrame(resolve));

        this.loaded = true;

        // 加载完成事件
        this.dispatchEvent({ type: "loaded" } as never);
    }

    // 超出绘制区域时不绘制
    frustumCulled = false;

    // 创建阴影
    castShadow: boolean = true;

    // 接受阴影
    receiveShadow: boolean = true;

    // promise.resole 队列
    resoles: Array<any> = [];
    // 等待加载加载完成
    async awaitLoaded() {
        if (!this.loaded) {
            await new Promise<void>((resolve) => {
                if (this.resoles.length === 0) {
                    (async () => {
                        while (!this.loaded) {
                            await new Promise(resolve => requestAnimationFrame(resolve));
                        }
                        while (this.resoles.length > 0) {
                            this.resoles.pop()();
                        }
                    })();
                }
                this.resoles.unshift(resolve);
            });
        }
    }

    clock = new Clock();

    //设置动画的权重
    async setClipActionWeight(weights: { [key: string]: number } = {}) {
        // 等待加载完成
        await this.awaitLoaded();
        for (let key in this.animationActions) {
            this.animationActions[key].weight = weights[key] || 0;
        }
    }

    //获取动画列表
    async getAnimas() {
        await this.awaitLoaded();
        return Object.keys(this.animationActions);
    }

    //播放指定动画
    async setAnimas(anima: string = "") {
        await this.awaitLoaded();
        this.setClipActionWeight({ [anima]: 1 });
    }

    //停止所有动画
    async stop() {
        await this.awaitLoaded();
        this.setClipActionWeight();
    }

    pathIndex = 0;
    paths: Array<Vector3> = [];
    async setPath(paths: Array<Vector3> = []) {
        await this.awaitLoaded();
        this.paths = paths;
        this.pathIndex = 0;
        if (paths[0]) {
            this.position.copy(paths[0]);

        } else {
            this.stop(); // 停止所有动画
        }
    }

    tmepObject = new Object3D();

    updateMatrix(): void {
        const delta = Math.min(this.clock.getDelta(), 2);
        //播放动画
        if (this.mixer) {
            this.mixer.update(delta);
        }

        // 深度应用属性
        this.traverse(obj => {
            const mesh = obj as Mesh;
            if (mesh.isMesh) {
                mesh.frustumCulled = this.frustumCulled;
                mesh.castShadow = this.castShadow;
                mesh.receiveShadow = this.receiveShadow;
            }
        });

        // 根据路径规划的线路移动
        if (this.paths.length) {
            const moveDirection = this.paths[this.pathIndex].clone().setY(this.position.y).sub(this.position).setLength(delta * 1.7);
            this.tmepObject.position.copy(this.position);
            let lookAt = this.paths[this.pathIndex].clone().setY(this.position.y).sub(this.position).multiplyScalar(-1).add(this.position);
            this.tmepObject.lookAt(lookAt);
            this.rotation.copy(this.tmepObject.rotation);
            this.position.add(moveDirection);
            if (this.position.distanceTo(this.paths[this.pathIndex].clone().setY(this.position.y)) < delta * 1.7) {
                this.pathIndex = ((this.pathIndex + 1) % this.paths.length);
                console.log("this.pathIndex", this.pathIndex);
            }

            this.setAnimas("Walk");
        } else {
            this.setAnimas("Idle");
        }

        this.dispatchEvent({ type: "move" } as never);

        super.updateMatrix();
    }



}